{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# FTVMQnnCanonicalize\n",
    "\n",
    "源码：`tvm/include/tvm/relay/qnn/transform.h`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```cpp\n",
    "/*!\n",
    " * \\brief Legalizes a QNN expr. Contains specifically two types of Legalizations. First,\n",
    " * converts/Lowers an expression containing QNN ops to an expression containing only core Relay ops.\n",
    " * Each QNN op is lowered to a sequence of exisiting Relay ops. This is a target-independent pass.\n",
    " * One can register the lowering/transformation function for this op using FTVMQnnCanonicalize\n",
    " * attr_name for FTVMLegalize op attribute. Second, as opposed to Relay Legalize, this one legalizes\n",
    " * only QNN ops. One can register a transformation/legalization function for an op by using the\n",
    " * FTVMQnnLegalize attr_name for FTVMLegalize op attribute. The isolation of QNN and Relay Legalize\n",
    " * gives us separation of concerns, leading to a better software practice. The legalization can be\n",
    " * configured to happen per target.\n",
    " *\n",
    " * \\return The pass.\n",
    " */\n",
    "TVM_DLL Pass Legalize();\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这段代码是一个名为 `Legalize` 的函数，它的作用是将 QNN 表达式合法化。具体来说，它包含两种类型的合法化：\n",
    "\n",
    "1. 将包含 QNN 算子的表达式转换为仅包含核心 Relay 算子的表达式。每个 QNN 算子都会被转换为一系列现有的 Relay 算子。这是一个与目标 target 无关的传递。可以使用 `FTVMQnnCanonicalize` 属性名称为 `FTVMLegalize` 算子属性注册 transformation/legalization 函数。\n",
    "\n",
    "2. 与 Relay Legalize 不同，这个函数只对 QNN 算子进行合法化。可以通过使用 `FTVMQnnLegalize` 属性名称为 `FTVMLegalize` 算子属性注册一个算子的 transformation/legalization 函数。QNN 和 Relay Legalize 的隔离使我们能够更好地分离关注点，从而得到更好的软件实践。合法化可以针对每个目标（target）进行配置。\n",
    "\n",
    "函数返回 Pass 对象。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## {func}`~tvm.relay.qnn.transform.CanonicalizeOps`\n",
    "\n",
    "源码：`tvm/python/tvm/relay/qnn/transform.py`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import testing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[0;31mSignature:\u001b[0m \u001b[0mCanonicalizeOps\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mDocstring:\u001b[0m\n",
      "Converts/Lowers an expression containing QNN ops to an expression containing only core\n",
      "(non-Dialect) Relay ops. Each QNN op is lowered to a sequence of existing Relay ops. This is a\n",
      "target-independent pass. One can register the lowering/transformation function for this op using\n",
      "FTVMQnnCanonicalize attr_name for FTVMLegalize op attribute.  An example of this transformation\n",
      "is below\n",
      "\n",
      "Examples\n",
      "________\n",
      "\n",
      ".. code-block:: python\n",
      "\n",
      "    # Original expression\n",
      "    qnn_expr = relay.qnn.op.requantize(y,\n",
      "                                       input_scale=1,\n",
      "                                       input_zero_point=0,\n",
      "                                       output_scale=1,\n",
      "                                       output_zero_point=0,\n",
      "                                       out_dtype='int8')\n",
      "\n",
      "    # We want to utilize all the existing Relay infrastructure. So, instead of supporting this\n",
      "    # QNN requantize op, we convert it into a sequence of existing Relay operators.\n",
      "    mod = tvm.IRModule.from_expr(qnn_expr)\n",
      "    mod = relay.qnn.transform.CanonicalizeOps()(mod)\n",
      "    relay_expr = mod['main']\n",
      "    print(relay_expr)\n",
      "\n",
      "    def @main(%quantized_data: Tensor[(200), int32]) -> Tensor[(200), int8] {\n",
      "      %0 = cast(%quantized_data, dtype=\"int64\") /* ty=Tensor[(200), int64] */;\n",
      "      %1 = multiply(%0, 2 /* ty=int64 */) /* ty=Tensor[(200), int64] */;\n",
      "      %2 = multiply(%1, 1073741824 /* ty=int64 */) /* ty=Tensor[(200), int64] */;\n",
      "      %3 = add(%2, 1073741824 /* ty=int64 */) /* ty=Tensor[(200), int64] */;\n",
      "      %4 = right_shift(%3, 31 /* ty=int64 */) /* ty=Tensor[(200), int64] */;\n",
      "      %5 = add(0 /* ty=int64 */, %4) /* ty=Tensor[(200), int64] */;\n",
      "      %6 = clip(%5, a_min=-128f, a_max=127f) /* ty=Tensor[(200), int64] */;\n",
      "      cast(%6, dtype=\"int8\") /* ty=Tensor[(200), int8] */\n",
      "    }\n",
      "\n",
      "Returns\n",
      "-------\n",
      "ret : tvm.transform.Pass\n",
      "    The registered pass that canonicalizes QNN ops to Relay ops.\n",
      "\u001b[0;31mFile:\u001b[0m      /media/pc/data/lxw/ai/tvm/python/tvm/relay/qnn/transform.py\n",
      "\u001b[0;31mType:\u001b[0m      function"
     ]
    }
   ],
   "source": [
    "from tvm.relay.qnn.transform import CanonicalizeOps\n",
    "\n",
    "CanonicalizeOps?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这段代码定义了一个名为 `CanonicalizeOps` 的函数，它的作用是将包含 QNN 算子的表达式转换为仅包含核心（非 Dialect）Relay 算子的表达式。每个 QNN 算子都会被转换为一系列现有的 Relay 算子。这是一个与目标无关的传递。\n",
    "\n",
    "函数返回 {class}`tvm.transform.Pass` 对象，该对象将 QNN 算子规范化为 Relay 算子。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以使用 `FTVMQnnCanonicalize` 属性名称为 `FTVMLegalize` 算子属性注册 lowering/transformation 函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[0;31mSignature:\u001b[0m \u001b[0mregister_qnn_canonicalize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mop_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlegal_op\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlevel\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mDocstring:\u001b[0m\n",
      "Register canonicalization function for a QNN op.\n",
      "\n",
      "This transforms QNN ops to mainline Relay components.\n",
      "\n",
      "Parameters\n",
      "----------\n",
      "op_name : str\n",
      "    The name of the operator\n",
      "\n",
      "legal_op: function (Attrs, List[Expr], List[relay.Type]) -> Expr\n",
      "    The function for transforming an expr to another expr.\n",
      "\n",
      "level : int\n",
      "    The priority level\n",
      "\u001b[0;31mFile:\u001b[0m      /media/pc/data/lxw/ai/tvm/python/tvm/relay/qnn/op/op.py\n",
      "\u001b[0;31mType:\u001b[0m      function"
     ]
    }
   ],
   "source": [
    "from tvm.relay.qnn.op import register_qnn_canonicalize\n",
    "\n",
    "register_qnn_canonicalize?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py312x",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
